home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / iguana / vts139b / lib / soundbla.pas < prev    next >
Pascal/Delphi Source File  |  1993-09-07  |  27KB  |  892 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:         SoundBlaster                                               }
  4. {                                                                            }
  5. { DESCRIPTION:    An UNIT that provides several routines for handling the    }
  6. {                 Sound Blaster and Sound Blaster Pro cards and compatibles. }
  7. {                                                                            }
  8. { AUTHOR:         Juan Carlos Arévalo                                        }
  9. {                                                                            }
  10. { MODIFICATIONS:  Nobody (yet ;-)                                            }
  11. {                                                                            }
  12. { HISTORY:        12-Nov-1992 Documentation.                                 }
  13. {                 26-Nov-1992 Included SB 16 support.                        }
  14. {                                                                            }
  15. { (C) 1992 VangeliSTeam                                                      }
  16. {____________________________________________________________________________}
  17.  
  18. UNIT SoundBlaster;
  19.  
  20. {$R-}
  21.  
  22. INTERFACE
  23.  
  24. USES SoundDevices;
  25.  
  26.  
  27.  
  28.  
  29. { I/O Port offsets. }
  30.  
  31. CONST
  32.   CMS1DataPortOffset = $00;  { CM/S 1-6  Data port.             Write Only. }
  33.   CMS1AddrPortOffset = $01;  { CM/S 1-6  Address port.          Write Only. }
  34.   CMS2DataPortOffset = $02;  { CM/S 7-12 Data port.             Write Only. }
  35.   CMS2AddrPortOffset = $03;  { CM/S 7-12 Address port.          Write Only. }
  36.  
  37.   MixAddrPortOffset  = $04;  { Mixer register port.             Write Only. }
  38.   MixDataPortOffset  = $05;  { Mixer data port.                 Read/Write. }
  39.  
  40.   FMStatPortOffset   = $08;  { Mono FM Status port.             Read  Only. }
  41.   FMAddrPortOffset   = $08;  { Mono FM Address port.            Write Only. }
  42.   FMDataPortOffset   = $09;  { Mono FM Data port.               Write Only. }
  43.  
  44.   LFMStatPortOffset  = $00;  { Left FM Status port.             Read  Only. }
  45.   LFMAddrPortOffset  = $00;  { Left FM Address port.            Write Only. }
  46.   LFMDataPortOffset  = $01;  { Left FM Data port.               Write Only. }
  47.  
  48.   RFMStatPortOffset  = $02;  { Right FM Status port.            Read  Only. }
  49.   RFMAddrPortOffset  = $02;  { Right FM Address port.           Write Only. }
  50.   RFMDataPortOffset  = $03;  { Right FM Data port.              Write Only. }
  51.  
  52.   DSPResetPortOffset = $06;  { DSP Reset port.                  Write Only. }
  53.   DSPReadPortOffset  = $0A;  { DSP Read data port.              Read  Only. }
  54.   DSPLifePortOffset  = $0A;  { DSP Read data port.              Read  Only. }
  55.   DSPWStatPortOffset = $0C;  { DSP Write buffer status port.    Write Only. }
  56.   DSPWritePortOffset = $0C;  { DSP Write data port.             Write Only. }
  57.   DSPRStatPortOffset = $0E;  { DSP Read buffer status port.     Read  Only. }
  58.   DSP8AckPortOffset  = $0E;  {  8 bit DMA IRQ Acknowledge port. Write Only. }
  59.   DSP16AckPortOffset = $0F;  { 16 bit DMA IRQ Acknowledge port. Write Only. }
  60.  
  61.   CDDataPortOffset   = $10;  { CD-ROM Data port.                Read  Only. }
  62.   CDCmdPortOffset    = $10;  { CD-ROM Command port.             Write Only. }
  63.   CDStatPortOffset   = $11;  { CD-ROM Status port.              Read  Only. }
  64.   CDResetPortOffset  = $12;  { CD-ROM Reset port.               Write Only. }
  65.   CDEnablePortOffset = $13;  { CD-ROM Enable port.              Write Only. }
  66.  
  67.  
  68. { I/O Ports. Same as above. }
  69.  
  70. CONST
  71.   CMS1DataPort : WORD    = $220 + CMS1DataPortOffset;
  72.   CMS1AddrPort : WORD    = $220 + CMS1AddrPortOffset;
  73.   CMS2DataPort : WORD    = $220 + CMS2DataPortOffset;
  74.   CMS2AddrPort : WORD    = $220 + CMS2AddrPortOffset;
  75.  
  76.   MixAddrPort  : WORD    = $220 + MixAddrPortOffset;
  77.   MixDataPort  : WORD    = $220 + MixDataPortOffset;
  78.  
  79.   FMStatPort   : WORD    = $220 + FMStatPortOffset;
  80.   FMAddrPort   : WORD    = $220 + FMAddrPortOffset;
  81.   FMDataPort   : WORD    = $220 + FMDataPortOffset;
  82.  
  83.   LFMStatPort  : WORD    = $220 + LFMStatPortOffset;
  84.   LFMAddrPort  : WORD    = $220 + LFMAddrPortOffset;
  85.   LFMDataPort  : WORD    = $220 + LFMDataPortOffset;
  86.  
  87.   RFMStatPort  : WORD    = $220 + RFMStatPortOffset;
  88.   RFMAddrPort  : WORD    = $220 + RFMAddrPortOffset;
  89.   RFMDataPort  : WORD    = $220 + RFMDataPortOffset;
  90.  
  91.   DSPResetPort : WORD    = $220 + DSPResetPortOffset;
  92.   DSPReadPort  : WORD    = $220 + DSPReadPortOffset;
  93.   DSPLifePort  : WORD    = $220 + DSPLifePortOffset;
  94.   DSPWStatPort : WORD    = $220 + DSPWStatPortOffset;
  95.   DSPWritePort : WORD    = $220 + DSPWritePortOffset;
  96.   DSPRStatPort : WORD    = $220 + DSPRStatPortOffset;
  97.   DSP8AckPort  : WORD    = $220 + DSP8AckPortOffset;
  98.   DSP16AckPort : WORD    = $220 + DSP16AckPortOffset;
  99.  
  100.   CDDataPort   : WORD    = $220 + CDDataPortOffset;
  101.   CDCmdPort    : WORD    = $220 + CDCmdPortOffset;
  102.   CDStatPort   : WORD    = $220 + CDStatPortOffset;
  103.   CDResetPort  : WORD    = $220 + CDResetPortOffset;
  104.   CDEnablePort : WORD    = $220 + CDEnablePortOffset;
  105.  
  106.  
  107. { Configuration. }
  108.  
  109. CONST
  110.   SbPort       : WORD    = $FFFF; { Base port. $FFFF Means Autodetect.      }
  111.   SbIrq        : WORD    = 7;     { DMA IRQ level.                          }
  112.   SbDMAChan    : WORD    = 1;     { DMA channel.                            }
  113.   SbDefTimeout : WORD    = 5000;  { Default DSP timeout.                    }
  114.   SbHiSpeed    : BOOLEAN = TRUE;  { User Desires HS DMA mode if TRUE.       }
  115.   SbForce      : BOOLEAN = FALSE; { Force TRUE the detection of the SB.     }
  116.   MixerForce   : BOOLEAN = FALSE; { Force TRUE the detection of the Mixer.  }
  117.   SbProForce   : BOOLEAN = FALSE; { Force TRUE the detection of the SB Pro. }
  118.   Sb16Force    : BOOLEAN = FALSE; { Force TRUE the detection of the SB 16.  }
  119.  
  120.  
  121. { Card information. }
  122.  
  123. CONST
  124.   SbVersionMin : BYTE       = 0;
  125.   SbVersionMaj : BYTE       = 0;
  126.   SbVersionStr : STRING[ 5] = '';
  127.   SbCopyright  : STRING[80] = '';
  128.   SbResponse1  : BYTE       = 0;
  129.   SbResponse2  : BYTE       = 0;
  130.  
  131. VAR
  132.   SbVersion    : WORD    ABSOLUTE SbVersionMin;
  133.  
  134.  
  135. { Run-time information. }
  136.  
  137. CONST
  138.   SbRegDetected     : BOOLEAN = FALSE;
  139.   SbRegInited       : BOOLEAN = FALSE;
  140.   SbProDetected     : BOOLEAN = FALSE;
  141.   SbProInited       : BOOLEAN = FALSE;
  142.   Sb16Detected      : BOOLEAN = FALSE;
  143.   Sb16Inited        : BOOLEAN = FALSE;
  144.   MixerDetected     : BOOLEAN = FALSE;
  145.  
  146.   SbWorksOk         : BOOLEAN = TRUE;  { Set to FALSE if DSP timeouts.         }
  147.   HSBlockSpecified  : WORD    = 0;     { Set to the last hi-speed block size.  }
  148.   Sb16BlockSpecified: WORD    = 0;     { Set to the last Sb 16 block size.     }
  149.   SbStereo          : BOOLEAN = FALSE; { Stereo DMA mode if TRUE.              }
  150.   SbFilter          : BOOLEAN = FALSE; { SB Pro output filter ON if TRUE.      }
  151.  
  152.   DoHiSpeed         : BOOLEAN = FALSE; { Hi speed DMA mode if TRUE.            }
  153.   Sb16Bit           : BOOLEAN = FALSE; { 16 bit output if TRUE.                }
  154.  
  155.   TimeConst         : BYTE    = 0;
  156.  
  157.   DMAStart          : BOOLEAN = FALSE;
  158.   DMAStop           : BOOLEAN = FALSE;
  159.   DMAStopped        : BOOLEAN = FALSE;
  160.  
  161.   DMAIrqWatch       : BYTE    = 0;
  162.  
  163.  
  164.  
  165. { DSP Commands. }
  166.  
  167. CONST
  168.   sdcSendOneSample  = $10;  { Send a sample to the DAC directly (mono mode only). }
  169.   sdcStartLSpeedDMA = $14;  { Start a low-speed DMA transfer.                     }
  170.   sdcSetTimeConst   = $40;  { Set the time constant.                              }
  171.   sdcSetHSpeedSize  = $48;  { Set hi-speed DMA transfer length.                   }
  172.   sdcStartHSpeedDMA = $91;  { Start a hi-speed DMA transfer.                      }
  173.   sdcTurnOnSpeaker  = $D1;  { Turn on the SB speaker.                             }
  174.   sdcTurnOffSpeaker = $D3;  { Turn off the SB speaker.                            }
  175.   sdcGetDSPVersion  = $E1;  { Get the DSP version number.                         }
  176.   sdcGetCopyright   = $E3;  { Get the card copyright string.                      }
  177.  
  178.  
  179. { Mixer registers. }
  180.  
  181. CONST
  182.   mxrDataReset    = $00;
  183.   mxrDACVolume    = $04;
  184.   mxrMicMixing    = $0A;
  185.   mxrInSetting    = $0C;
  186.   mxrOutSetting   = $0E;
  187.   mxrMasterVolume = $22;
  188.   mxrFMVolume     = $26;
  189.   mxrCDVolume     = $28;
  190.   mxrLineVolume   = $2E;
  191.  
  192.  
  193. { Bit masks for the mixer registers. }
  194.  
  195. CONST
  196.   mxiFilterVal = $38;
  197.   mxiADCVal    = $06;
  198.   mxoFilterNeg = $20;
  199.   mxoStereoOn  = $02;
  200.  
  201. TYPE
  202.   TMixerVolume = (mvMaster,
  203.                   mvVoice,
  204.                   mvFM,
  205.                   mvLine,
  206.                   mvMicrophone,
  207.                   mvSpeaker,
  208.                   mvCD);
  209.  
  210. CONST
  211.   SbProRegs : ARRAY[mvMaster..mvCD] OF BYTE = ( $22, $04, $26, $2E, $0A, $00, $28 );
  212.   Sb16Regs  : ARRAY[mvMaster..mvCD] OF BYTE = ( $30, $32, $34, $38, $3A, $3B, $34 );
  213.  
  214.  
  215.  
  216.  
  217. { SB basic }
  218.  
  219. FUNCTION  SbReset                                       : BOOLEAN;
  220.  
  221. PROCEDURE SbWriteLoop    (t: WORD);
  222. PROCEDURE SbWriteByte    (t: WORD; b: BYTE);
  223. PROCEDURE SbReadLoop     (t: WORD);
  224. FUNCTION  SbReadByte     (t: WORD)                      : BYTE;
  225.  
  226.  
  227. { Mixer basic }
  228.  
  229. PROCEDURE SbWriteMixerReg (Reg, Val: BYTE);
  230. FUNCTION  SbReadMixerReg  (Reg: BYTE)                    : BYTE;
  231.  
  232.  
  233. { SB Reg }
  234.  
  235. FUNCTION  SbRegDetect : BOOLEAN;
  236. PROCEDURE SbRegInit;
  237. PROCEDURE SbRegDone;
  238.  
  239. PROCEDURE SbGetDSPVersion;
  240. PROCEDURE SbGetCopyrightString;
  241. PROCEDURE SbSetTimeConst    (tc: BYTE);
  242. PROCEDURE SbUpdateTimeConst;
  243. PROCEDURE SbStartSampleLS   (Len: WORD; Cont: BOOLEAN);
  244. PROCEDURE SbStartSampleHS   (Len: WORD; Cont: BOOLEAN);
  245. PROCEDURE SbPlaySample      (Len: WORD; Cont: BOOLEAN);
  246.  
  247.  
  248. { Mixer }
  249.  
  250. FUNCTION MixerDetect : BOOLEAN;
  251.  
  252. PROCEDURE MixerSetVolume(Reg: TMixerVolume;     VolLeft, VolRight: BYTE);
  253. FUNCTION  MixerGetVolume(Reg: TMixerVolume; VAR VolLeft, VolRight: BYTE) : BOOLEAN;
  254.  
  255.  
  256. { SB Pro }
  257.  
  258. FUNCTION  SbProDetect : BOOLEAN;
  259. PROCEDURE SbProInit;
  260. PROCEDURE SbProDone;
  261.  
  262. PROCEDURE SbProSetStereo (Stereo: BOOLEAN);
  263. PROCEDURE SbProSetFilter (Filter: BOOLEAN);
  264.  
  265.  
  266. { SB 16 }
  267.  
  268. FUNCTION  Sb16Detect : BOOLEAN;
  269. PROCEDURE Sb16Init;
  270. PROCEDURE Sb16Done;
  271.  
  272. PROCEDURE Sb16StartSample(Len: WORD; Cont: BOOLEAN);
  273.  
  274.  
  275.  
  276.  
  277. IMPLEMENTATION
  278.  
  279. USES Debugging;
  280.  
  281.  
  282.  
  283. {----------------------------------------------------------------------------}
  284. { Sound Blaster basic routines.                                              }
  285. {____________________________________________________________________________}
  286.  
  287. FUNCTION SbReset : BOOLEAN;
  288.   CONST
  289.     ready = $AA;
  290.   VAR
  291.     ct, stat : BYTE;
  292.   BEGIN
  293.     PORT[DSPResetPort] := 1;
  294.     FOR ct := 1 TO 100 DO;
  295.     PORT[DSPResetPort] := 0;
  296.  
  297.     stat := 0;
  298.     ct   := 0;
  299.     WHILE (stat <> ready) AND (ct < 100) DO BEGIN
  300.       stat := PORT[DSPRStatPort];
  301.       stat := PORT[DSPReadPort];
  302.       INC(ct);
  303.     END;
  304.  
  305.     SbReset := stat = ready;
  306.   END;
  307.  
  308.  
  309. PROCEDURE SbWriteLoop(t: WORD); ASSEMBLER;
  310.   ASM
  311.  
  312.                 MOV     BX,t
  313.                 MOV     DX,[DSPWritePort]
  314. @@lp:            DEC    BX
  315.                  JZ     @@fin
  316.                  IN     AL,DX
  317.                  ADD    AL,AL
  318.                  JC     @@lp
  319. @@fin:          OR      BL,BH
  320.                 MOV     [SbWorksOk],BL
  321.   END;
  322.  
  323.  
  324. PROCEDURE SbWriteByte(t: WORD; b: BYTE); ASSEMBLER;
  325.   ASM
  326.  
  327.                 MOV     AL,b
  328.                 XOR     AH,AH
  329.                 PUSH    AX
  330.                 PUSH    $60
  331.                 CALL    WriteSNum
  332.  
  333.                 MOV     AX,t
  334.                 PUSH    AX
  335.                 CALL    SbWriteLoop
  336.                 JNZ     @@ya
  337.  
  338.                 MOV     DX,[DSPLifePort]
  339.                 IN      AL,DX
  340.  
  341.                 MOV     AX,t
  342.                 PUSH    AX
  343.                 CALL    SbWriteLoop
  344.  
  345. @@ya:           MOV     AL,b
  346.                 OUT     DX,AL
  347.  
  348.                 MOV     AL,[SbWorksOk]
  349.                 ADD     AL,'A'
  350.                 XOR     AH,AH
  351.                 PUSH    AX
  352.                 PUSH    $40
  353.                 CALL    WriteChar
  354.  
  355.   END;
  356.  
  357.  
  358. PROCEDURE SbReadLoop(t: WORD); ASSEMBLER;
  359.   ASM
  360.  
  361.                 MOV     BX,t
  362.                 MOV     DX,[DSPRStatPort]
  363. @@lp:            DEC    BX
  364.                  JZ     @@fin
  365.                  IN     AL,DX
  366.                  ADD    AL,AL
  367.                  JNC    @@lp
  368. @@fin:          OR      BL,BH
  369.                 MOV     [SbWorksOk],BL
  370.                 MOV     DX,[DSPReadPort]
  371.   END;
  372.  
  373.  
  374. FUNCTION SbReadByte(t: WORD) : BYTE; ASSEMBLER;
  375.   ASM
  376.                 MOV     AX,t
  377.                 PUSH    AX
  378.                 CALL    SbReadLoop
  379.                 JNZ     @@ya
  380. {
  381.                 MOV     DX,[DSPLifePort]
  382.                 IN      AL,DX
  383.  
  384.                 MOV     AX,t
  385.                 PUSH    AX
  386.                 CALL    SbReadLoop
  387. }
  388. @@ya:           IN      AL,DX
  389.   END;
  390.  
  391.  
  392.  
  393.  
  394. {----------------------------------------------------------------------------}
  395. { Mixer basic routines.                                                      }
  396. {____________________________________________________________________________}
  397.  
  398. PROCEDURE SbWriteMixerReg(Reg, Val: BYTE); ASSEMBLER;
  399.   ASM
  400.  
  401.                 MOV     DX,[MixAddrPort]
  402.                 MOV     AL,[Reg]
  403.                 OUT     DX,AL
  404.  
  405.                 MOV     DX,[MixDataPort]
  406.                 MOV     AL,[Val]
  407.                 OUT     DX,AL
  408.  
  409.   END;
  410.  
  411.  
  412. FUNCTION SbReadMixerReg(Reg: BYTE) : BYTE; ASSEMBLER;
  413.   ASM
  414.  
  415.                 MOV     DX,[MixAddrPort]
  416.                 MOV     AL,[Reg]
  417.                 OUT     DX,AL
  418.  
  419.                 MOV     DX,[MixDataPort]
  420.                 IN      AL,DX
  421.  
  422.   END;
  423.  
  424.  
  425.  
  426.  
  427. {----------------------------------------------------------------------------}
  428. { Regular Sound Blaster generic routines.                                    }
  429. {____________________________________________________________________________}
  430.  
  431. FUNCTION SbRegDetect : BOOLEAN;
  432.   VAR
  433.     Port, Lst : WORD;
  434.   BEGIN
  435.  
  436.     SbRegDetect := SbRegDetected;
  437.  
  438.     IF SbRegDetected THEN EXIT;
  439.  
  440.     IF SbPort < $8000 THEN
  441.       BEGIN
  442.         Port := SbPort;
  443.         Lst  := SbPort;
  444.       END
  445.     ELSE
  446.       BEGIN
  447.         Port := $210;
  448.         Lst  := $280;
  449.       END;
  450.  
  451.     WHILE (NOT SbRegDetected) AND (Port <= Lst) DO BEGIN
  452.       CMS1DataPort := Port + CMS1DataPortOffset;
  453.       CMS1AddrPort := Port + CMS1AddrPortOffset;
  454.       CMS2DataPort := Port + CMS2DataPortOffset;
  455.       CMS2AddrPort := Port + CMS2AddrPortOffset;
  456.  
  457.       MixAddrPort  := Port + MixAddrPortOffset;
  458.       MixDataPort  := Port + MixDataPortOffset;
  459.  
  460.       FMStatPort   := Port + FMStatPortOffset;
  461.       FMAddrPort   := Port + FMAddrPortOffset;
  462.       FMDataPort   := Port + FMDataPortOffset;
  463.  
  464.       LFMStatPort  := Port + LFMStatPortOffset;
  465.       LFMAddrPort  := Port + LFMAddrPortOffset;
  466.       LFMDataPort  := Port + LFMDataPortOffset;
  467.  
  468.       RFMStatPort  := Port + RFMStatPortOffset;
  469.       RFMAddrPort  := Port + RFMAddrPortOffset;
  470.       RFMDataPort  := Port + RFMDataPortOffset;
  471.  
  472.       DSPResetPort := Port + DSPResetPortOffset;
  473.       DSPReadPort  := Port + DSPReadPortOffset;
  474.       DSPLifePort  := Port + DSPLifePortOffset;
  475.       DSPWStatPort := Port + DSPWStatPortOffset;
  476.       DSPWritePort := Port + DSPWritePortOffset;
  477.       DSPRStatPort := Port + DSPRStatPortOffset;
  478.       DSP8AckPort  := Port + DSP8AckPortOffset;
  479.       DSP16AckPort := Port + DSP16AckPortOffset;
  480.  
  481.       CDDataPort   := Port + CDDataPortOffset;
  482.       CDCmdPort    := Port + CDCmdPortOffset;
  483.       CDStatPort   := Port + CDStatPortOffset;
  484.       CDResetPort  := Port + CDResetPortOffset;
  485.       CDEnablePort := Port + CDEnablePortOffset;
  486.  
  487.       SbRegDetected := SbReset;
  488.  
  489.       IF NOT SbRegDetected THEN INC(Port, $10);
  490.     END;
  491.  
  492.     SbRegDetect := SbRegDetected;
  493.  
  494.   END;
  495.  
  496.  
  497. PROCEDURE SbRegInit;
  498.   BEGIN
  499.  
  500.     IF NOT SbRegDetect THEN EXIT;
  501.  
  502.     IF NOT SbRegInited THEN
  503.       BEGIN
  504. (*
  505.         SbWriteByte(SbDefTimeout, $E0);
  506.         SbWriteByte(SbDefTimeout, $AA);
  507.         SbResponse1 := SbReadByte (SbDefTimeout); { $55 }
  508.         SbWriteByte(SbDefTimeout, $E4);
  509.         SbWriteByte(SbDefTimeout, $AA);
  510.         SbWriteByte(SbDefTimeout, $E8);
  511.         SbResponse2 := SbReadByte (SbDefTimeout); { $AA }
  512. *)
  513.         SbGetDSPVersion;
  514.  
  515.         DoHiSpeed := (SbVersion > $200) AND SbHiSpeed {AND FALSE};
  516. {
  517.         IF DoHiSpeed THEN
  518.           BEGIN
  519.             SbWriteByte(SbDefTimeout, $48);
  520.             SbWriteByte(SbDefTimeout, $00);
  521.             SbWriteByte(SbDefTimeout, $00);
  522.             SbWriteByte(SbDefTimeout, $91);
  523.           END;
  524. }
  525.         SbWriteByte(SbDefTimeout, sdcTurnOnSpeaker);
  526.  
  527.       END;
  528.  
  529.     SbRegInited := TRUE;
  530.  
  531.   END;
  532.  
  533.  
  534. PROCEDURE SbRegDone;
  535.   BEGIN
  536.     IF NOT (SbRegDetected AND SbRegInited) THEN EXIT;
  537. {    SbWriteByte(SbDefTimeout, sdcTurnOffSpeaker);}
  538.     SbRegDetected := FALSE;
  539.     SbRegInited   := FALSE;
  540.   END;
  541.  
  542.  
  543.  
  544.  
  545. PROCEDURE SbGetDSPVersion;
  546.   VAR
  547.     i : WORD;
  548.     t : WORD;
  549.     s : STRING[2];
  550.   BEGIN
  551.     SbWriteByte(SbDefTimeout, sdcGetDSPVersion); { Send command. }
  552.     t := 0;
  553.     REPEAT
  554.       SbVersionMaj := SbReadByte($FFFF);
  555.       INC(t);
  556.     UNTIL ((SbVersionMaj <> $AA) AND SbWorksOk) OR (t >= 10);
  557.     SbVersionMin := SbReadByte(SbDefTimeout);
  558.  
  559.     STR(SbVersionMaj, SbVersionStr);
  560.     SbVersionStr := SbVersionStr + '.';
  561.     STR(SbVersionMin, s);
  562.     IF SbVersionMin > 9 THEN SbVersionStr := SbVersionStr +       s
  563.                         ELSE SbVersionStr := SbVersionStr + '0' + s;
  564.   END;
  565.  
  566.  
  567. PROCEDURE SbGetCopyrightString;
  568.   VAR
  569.     t : WORD;
  570.   BEGIN
  571.     SbWriteByte(SbDefTimeout, sdcGetCopyright); { Send command. }
  572.     t := 0;
  573.     REPEAT
  574.       SbCopyright := CHAR(SbReadByte($FFFF));
  575.       INC(t);
  576.     UNTIL ((SbCopyright[1] <> #$AA) AND SbWorksOk) OR (t = 10);
  577.  
  578.     WHILE SbWorksOk AND (Length(SbCopyright) < 80) DO
  579.       SbCopyright := SbCopyright + CHAR(SbReadByte(SbDefTimeout));
  580.  
  581.     DEC(SbCopyright[0]);
  582.   END;
  583.  
  584.  
  585. PROCEDURE SbSetTimeConst(tc: BYTE);
  586.   BEGIN
  587.     IF Sb16Detected THEN
  588.       BEGIN
  589.         IF Sb16Bit THEN
  590.           SbWriteByte(SbDefTimeout, $D9)  { Send time constant command.             }
  591.         ELSE
  592.           SbWriteByte(SbDefTimeout, $DA); { Send time constant command.             }
  593.       END;
  594.     SbWriteByte(SbDefTimeout,   sdcSetTimeConst); { Send time constant command.             }
  595.     SbWriteByte(SbDefTimeout*4, tc);              { Send the time constant.                 }
  596.     TimeConst := 0;                               { Reset time constant to already changed. }
  597.     IF Sb16Detected THEN
  598.       IF Sb16Bit THEN
  599.         SbWriteByte(SbDefTimeout, $47)  { Send time constant command.             }
  600.       ELSE
  601.         SbWriteByte(SbDefTimeout, $45); { Send time constant command.             }
  602.   END;
  603.  
  604.  
  605. PROCEDURE SbUpdateTimeConst;
  606.   BEGIN
  607.     IF TimeConst = 0 THEN EXIT;                 { If not changed then do nothing.         }
  608.     SbSetTimeConst(TimeConst);
  609.   END;
  610.  
  611.  
  612. PROCEDURE SbStartSampleLS(Len: WORD; Cont: BOOLEAN);
  613.   BEGIN
  614.     HSBlockSpecified := 0;   { Reset Hi-speed block specifier, just in case. }
  615.  
  616.     SbWriteByte(SbDefTimeout, sdcStartLSpeedDMA); { Start DMA low speed command.   }
  617.     SbWriteByte(SbDefTimeout, LO(Len));           { Low & high bytes of size.      }
  618.     SbWriteByte(SbDefTimeout, HI(Len));
  619.   END;
  620.  
  621.  
  622. PROCEDURE SbStartSampleHS(Len: WORD; Cont: BOOLEAN);
  623.   BEGIN
  624.     IF HSBlockSpecified <> Len THEN Cont := FALSE;
  625.     IF NOT Cont THEN
  626.       BEGIN
  627.         SbWriteByte(SbDefTimeout, sdcSetHSpeedSize);  { Set hi speed DMA block command. }
  628.         SbWriteByte(SbDefTimeout, LO(Len));           { Low & high bytes of size.       }
  629.         SbWriteByte(SbDefTimeout, HI(Len));
  630.         HSBlockSpecified := Len;
  631.       END;
  632.  
  633.     IF NOT (Sb16Detected AND Cont) THEN
  634.       SbWriteByte(SbDefTimeout, sdcStartHSpeedDMA); { Start DMA in hi speed mode.    }
  635.   END;
  636.  
  637.  
  638. PROCEDURE SbPlaySample(Len: WORD; Cont: BOOLEAN);
  639.   BEGIN
  640.  
  641.     IF Len < 10 THEN EXIT;   { Too short -> Discard. It wouldn't sound anyway. }
  642.  
  643.     IF SbStereo THEN INC(Len, Len); { Twice as big a buffer if stereo mode. }
  644.     DEC(Len);                       { DMA sizes are always size - 1.        }
  645.  
  646.     IF Sb16Detected AND (SbStereo OR Sb16Bit) THEN
  647.       Sb16StartSample(Len, Cont)
  648.     ELSE IF DoHiSpeed THEN
  649.       SbStartSampleHS(Len, Cont)
  650.     ELSE
  651.       SbStartSampleLS(Len, Cont);
  652.   END;
  653.  
  654.  
  655.  
  656.  
  657. {----------------------------------------------------------------------------}
  658. { Mixer generic routines.                                                    }
  659. {____________________________________________________________________________}
  660.  
  661. FUNCTION MixerDetect : BOOLEAN;
  662.   VAR
  663.     SaveReg : WORD;
  664.     NewReg  : WORD;
  665.   BEGIN
  666.     MixerDetect := MixerDetected;
  667.     IF NOT SbRegDetect OR MixerDetected THEN EXIT;
  668.  
  669.     SaveReg := SbReadMixerReg($22);
  670.     SbWriteMixerReg($22, 243);
  671.     NewReg  := SbReadMixerReg($22);
  672.  
  673.     IF NewReg = 243 THEN
  674.       MixerDetected := TRUE;
  675.  
  676.     SbWriteMixerReg($22, SaveReg);
  677.  
  678.     MixerDetect := MixerDetected;
  679.   END;
  680.  
  681.  
  682.  
  683.  
  684. PROCEDURE MixerSetVolume(Reg: TMixerVolume; VolLeft, VolRight: BYTE);
  685.   VAR
  686.     Addr   : BYTE;
  687.     VolMax : BYTE;
  688.   BEGIN
  689.     IF NOT MixerDetected THEN EXIT;
  690.  
  691.     IF Sb16Detected THEN Addr := Sb16Regs [Reg]
  692.                     ELSE Addr := SbProRegs[Reg];
  693.  
  694.     IF VolLeft > VolRight THEN VolMax := VolLeft
  695.                           ELSE VolMax := VolRight;
  696.  
  697.     CASE Reg OF
  698.       mvMicrophone : BEGIN
  699.                        IF Sb16Detected THEN SbWriteMixerReg(Addr, VolMax)
  700.                                        ELSE SbWriteMixerReg(Addr, VolMax SHR 5);
  701.                      END;
  702.       mvSpeaker    : BEGIN
  703.                        IF Sb16Detected THEN SbWriteMixerReg(Addr, VolMax);
  704.                      END;
  705.     ELSE
  706.  
  707.       IF Sb16Detected THEN
  708.         BEGIN
  709.           SbWriteMixerReg(Addr,     VolLeft);
  710.           SbWriteMixerReg(Addr + 1, VolRight);
  711.         END
  712.       ELSE
  713.         SbWriteMixerReg(Addr, (VolLeft  AND $F0) +
  714.                               (VolRight SHR   4));
  715.  
  716.     END;
  717.  
  718.   END;
  719.  
  720.  
  721. FUNCTION MixerGetVolume(Reg: TMixerVolume; VAR VolLeft, VolRight: BYTE) : BOOLEAN;
  722.   VAR
  723.     Addr   : BYTE;
  724.     VolMax : BYTE;
  725.   BEGIN
  726.     MixerGetVolume := FALSE;
  727.  
  728.     IF NOT MixerDetected THEN EXIT;
  729.  
  730.     IF Sb16Detected THEN Addr := Sb16Regs [Reg]
  731.                     ELSE Addr := SbProRegs[Reg];
  732.  
  733.     VolLeft  := 0;
  734.     VolRight := 0;
  735.  
  736.     MixerGetVolume := TRUE;
  737.  
  738.     CASE Reg OF
  739.       mvMicrophone : BEGIN
  740.                        IF Sb16Detected THEN VolLeft := SbReadMixerReg(Addr)
  741.                                        ELSE VolLeft := SbReadMixerReg(Addr) SHL 5;
  742.                        VolRight := VolLeft;
  743.                      END;
  744.       mvSpeaker    : BEGIN
  745.                        IF Sb16Detected THEN VolLeft := SbReadMixerReg(Addr)
  746.                                        ELSE MixerGetVolume := FALSE;
  747.                        VolRight := VolLeft;
  748.                      END;
  749.     ELSE
  750.  
  751.       IF Sb16Detected THEN
  752.         BEGIN
  753.           VolLeft  := SbReadMixerReg(Addr);
  754.           VolRight := SbReadMixerReg(Addr + 1);
  755.         END
  756.       ELSE
  757.         BEGIN
  758.           VolLeft  := SbReadMixerReg(Addr);
  759.           VolRight := VolLeft SHL 4;
  760.           VolLeft  := VolLeft AND $F0;
  761.         END;
  762.  
  763.     END;
  764.  
  765.   END;
  766.  
  767.  
  768.  
  769.  
  770. {----------------------------------------------------------------------------}
  771. { Sound Blaster Pro generic routines.                                        }
  772. {____________________________________________________________________________}
  773.  
  774. FUNCTION SbProDetect : BOOLEAN;
  775.   BEGIN
  776.     SbProDetect := SbProDetected;
  777.     IF SbProDetected THEN EXIT;
  778.  
  779.     IF NOT SbRegInited THEN SbRegInit;
  780.  
  781.     SbProDetected := SbRegDetect AND MixerDetect AND (SbVersion < $400);
  782.     SbProDetect   := SbProDetected;
  783.   END;
  784.  
  785.  
  786. PROCEDURE SbProInit;
  787.   BEGIN
  788.     IF NOT SbProDetect THEN EXIT;
  789.     SbProInited := TRUE;
  790.   END;
  791.  
  792.  
  793. PROCEDURE SbProDone;
  794.   BEGIN
  795.     SbRegDone;
  796.   END;
  797.  
  798.  
  799.  
  800.  
  801. PROCEDURE SbProSetStereo(Stereo: BOOLEAN);
  802.   VAR
  803.     i : BYTE;
  804.   BEGIN
  805.     IF NOT SbProDetected THEN EXIT;
  806.     SbStereo := Stereo;
  807.     i := SbReadMixerReg(mxrOutSetting);
  808.     SbWriteMixerReg(mxrOutSetting, (i      AND NOT mxoStereoOn) +
  809.                                    (BYTE(Stereo) * mxoStereoOn));
  810.   END;
  811.  
  812.  
  813. PROCEDURE SbProSetFilter(Filter: BOOLEAN);
  814.   VAR
  815.     i : BYTE;
  816.   BEGIN
  817.     IF NOT SbProDetected THEN EXIT;
  818.     SbFilter := Filter;
  819.     i := SbReadMixerReg(mxrOutSetting);
  820.     SbWriteMixerReg(mxrOutSetting, (i      AND NOT mxoFilterNeg) +
  821.                                    (BYTE(Filter) * mxoFilterNeg));
  822.   END;
  823.  
  824.  
  825.  
  826.  
  827. {----------------------------------------------------------------------------}
  828. { Sound Blaster 16 generic routines.                                         }
  829. {____________________________________________________________________________}
  830.  
  831. FUNCTION Sb16Detect : BOOLEAN;
  832.   BEGIN
  833.     Sb16Detect := Sb16Detected;
  834.     IF Sb16Detected THEN EXIT;
  835.  
  836.     IF NOT SbRegInited THEN SbRegInit;
  837.  
  838.     Sb16Detected := SbRegDetect AND MixerDetect AND (SbVersion >= $400);
  839.     Sb16Detect   := Sb16Detected;
  840.   END;
  841.  
  842.  
  843. PROCEDURE Sb16Init;
  844.   BEGIN
  845.     IF NOT Sb16Detect THEN EXIT;
  846.  
  847.     SbGetCopyrightString;
  848.  
  849.     Sb16Inited := TRUE;
  850.   END;
  851.  
  852.  
  853. PROCEDURE Sb16Done;
  854.   BEGIN
  855.     SbRegDone;
  856.   END;
  857.  
  858.  
  859.  
  860.  
  861. PROCEDURE Sb16StartSample(Len: WORD; Cont: BOOLEAN);
  862.   BEGIN
  863.  
  864.     IF (NOT Cont) OR (Sb16BlockSpecified <> Len){ OR TRUE }THEN
  865.       BEGIN
  866.         IF Sb16Bit THEN
  867.           SbWriteByte(SbDefTimeout, $B6)    { Set 16 bit DMA transfer command. }
  868.         ELSE
  869.           SbWriteByte(SbDefTimeout, $C6);   { Set  8 bit DMA transfer command. }
  870.         IF SbStereo THEN
  871.           SbWriteByte(SbDefTimeout, $20)    { Set stereo mode.                 }
  872.         ELSE
  873.           SbWriteByte(SbDefTimeout, $00);   { Set mono mode.                   }
  874.         SbWriteByte(SbDefTimeout, LO(Len));
  875.         SbWriteByte(SbDefTimeout, HI(Len)); { Low & high bytes of size.        }
  876.         Sb16BlockSpecified := Len;
  877.       END
  878.     ELSE
  879.       BEGIN
  880.         IF Sb16Bit THEN
  881.           SbWriteByte(SbDefTimeout, $47)    { 16 bit DMA continue command. }
  882.         ELSE
  883.           SbWriteByte(SbDefTimeout, $45);   {  8 bit DMA continue command. }
  884.       END;
  885.  
  886.   END;
  887.  
  888.  
  889.  
  890.  
  891. END.
  892.